package cn.felord.common.security;

import cn.felord.common.bean.IdentifierEnum;
import cn.felord.common.bean.RestBody;
import cn.felord.common.service.ISysRoleService;
import cn.hutool.core.util.ArrayUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author Dax
 * @since 17:49  2018/9/20
 */
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    private static final String AUTHENTICATION_PREFIX = "Bearer ";
    private static final String AUTHENTICATION_HEADER = "Authorization";
    @Resource
    private JwtTokenGenerator jwtTokenGenerator;
    @Resource
    private JwtTokenStorage jwtTokenStorage;
    @Resource
    private ISysRoleService iSysRoleService;

    //    @Resource
//    private ISysUserService iSysUserService;
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader(AUTHENTICATION_HEADER);

        if (header == null || !header.startsWith(AUTHENTICATION_PREFIX)) {
            if (response.isCommitted()) {
                log.debug("Response has already been committed");
                return;
            }

            Map<String, Object> map = new HashMap<>();
            map.put("time", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            map.put("uri", request.getAttribute("javax.servlet.error.request_uri"));

            response.setStatus(HttpServletResponse.SC_OK);
            response.setCharacterEncoding("utf-8");
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            ObjectMapper objectMapper = new ObjectMapper();
            String resBody = objectMapper.writeValueAsString(RestBody.build(HttpServletResponse.SC_UNAUTHORIZED, map, "U are UNAUTHORIZED", IdentifierEnum.DISABLE));
            PrintWriter printWriter = response.getWriter();
            printWriter.print(resBody);
            printWriter.flush();
            printWriter.close();
            return;
        }
        String jwtToken = header.replace(AUTHENTICATION_PREFIX, "");

        authenticationTokenHandle(jwtToken, request);

        chain.doFilter(request, response);

    }

    private void authenticationTokenHandle(String jwtToken, HttpServletRequest request) {

        if (StringUtils.hasText(jwtToken)) {
            JwtClaims jwtClaims = jwtTokenGenerator.decodeAndVerify(jwtToken);
            if (Objects.nonNull(jwtClaims)) {
                String userId = jwtClaims.getAud();
                String expireTime = jwtClaims.getExp();
                if (isExpired(expireTime)) {
                    return;
                }
                 // 从缓存获取 token
                JwtEntity jwtEntity = jwtTokenStorage.get(userId);
                 if (Objects.isNull(jwtEntity)){
                     return;
                 }
                String access_token = jwtEntity.getAccess_token();

                if (jwtToken.equals(access_token)) {
                    Optional<Set<String>> optional = Optional.ofNullable(iSysRoleService.selectUserRoleByUserId(userId));
                    Set<String> roleNames = optional.orElse(new HashSet<>());
                    List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(ArrayUtil.toArray(roleNames, String.class));
                    SecureUser user = new SecureUser(userId, "TOKEN_USER", "[PROTECTED]", authorities);
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, null, authorities);
                    usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            }

        }

    }

    /**
     *
     * @param exp
     * @return
     */
    private boolean isExpired(String exp) {
        return LocalDateTime.now().isAfter(LocalDateTime.parse(exp, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }


}
